"""
!/usr/bin/env python
-*- coding: utf-8 -*-
@Author  : MarkJia
@Site    : 
@File    : 12、显式等待的高级用法.py

"""

"""
显式等待原理
    显性等待 ： 在代码中定义等待一定条件发生后再进行下一步执行代码。
    在最长等待时间内循环执行结束条件的条件
    WebDriverWait(driver实例，最长等待时间，轮询时间).until(结束条件函数)
excepted_conditions
    演示地址： https://vip.ceshiren.com/#/ui_study
封装等待条件
"""

import time
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException


# ======================================    until 源码如下： =============================================
def until(self, method, message=''):
    screen = None
    stacktrace = None
    # 定义一个结束时间：当前时间 + 超时时间
    # 设置超时时间
    end_time = time.time() + self._timeout
    while True:
        try:
            # 调用了传入进来的函数对象，并且传入了driver参数
            # 执行传入的函数参数，获取执行结果。如果返回为真，则返回函数的执行结果
            value = method(self._driver)
            if value:
                return value
        except self._ignored_exceptions as exc:
            screen = getattr(exc, 'screen', None)
            stacktrace = getattr(exc, 'stacktrace', None)
        # 每隔 0.5 s 会轮询执行   -> 每次循环间隔多久
        time.sleep(self._poll)
        # 当前时间 > 结束时间。会跳出循环，并抛超时异常
        if time.time() > end_time:  # 如果超时，跳出循环
            break
    raise TimeoutException(message, screen, stacktrace)  # 循环正常结束，超时异常


"""
执行结果：代码执行5次，抛出 时间超时
当前的时间为 1664205217.0018642
当前的时间为 1664205219.0018713
当前的时间为 1664205221.0024028
当前的时间为 1664205223.0025263
当前的时间为 1664205225.002937
Traceback (most recent call last):
  File "D:\Project_Workspace\Auto\webtest\12、显式等待的高级用法.py", line 62, in <module>
    WebDriverWait(driver, 10, 2).until(fake_conditions, '超时信息')
  File "D:\Python\lib\site-packages\selenium\webdriver\support\wait.py", line 90, in until
    raise TimeoutException(message, screen, stacktrace)
selenium.common.exceptions.TimeoutException: Message: 超时信息
"""

# 初识显式等待
if __name__ == '__main__':
    driver = 'aaa'


    # 函数没有返回值，所有 源码的 value 没值
    def fake_conditions(driver):
        print('当前的时间为', time.time())


    # until 传入的参数是一个函数对象，不是函数调用
    WebDriverWait(driver, 10, 2).until(fake_conditions, '超时信息')

#   ======================   显示等待 ： expected_conditions (常见使用类型) =======================================
"""
element ： 
- element_to_be_clickable: 判断元素是否可以被点击
- visibility_of_element_located: 判断元素是否可见

url
- url_contains(): 针对于url

title
- title_is(): 针对于标题

frame
- frame_to_be_available_and_switch_to_it(locator): 针对于frame

alert
- alert_is_present() ：针对于弹框
"""


def wait_show():
    driver = webdriver.Chrome()
    driver.get('https://vip.ceshiren.com/#/ui_study')
    # 确定返回值是否为 webelement 对象：要点到 expected_conditions 中的源码进行查看
    # 说明：不是所有的 expected_conditions 的返回值都是 webelement 对象
    # 等待元素是否可以被点击，expected_conditions 的参数传入都是一个元组
    # 点击的元素可见
    WebDriverWait(driver, 10).until(
        expected_conditions.element_to_be_clickable((By.CSS_SELECTOR, '#success_btn'))).click()
    # driver.find_element(By.CSS_SELECTOR,"#success_btn").click() # 没有显示等待（没有提示信息）
    time.sleep(5)


if __name__ == '__main__':
    wait_show()

#  ============================   封装显式等待 - 封装等待条件  ======================================
"""
官方的 excepted_conditions 不可能覆盖所有场景
定制封装条件更加灵活，可控
"""

# 需求： 一直点击按钮，直到下一个页面出现为止
# 输入一：点击的目标按钮  输入二：下一个页面出现的元素

# =========================  参考源码  ============================
"""
def _predicate(driver):
    target = mark
    if not isinstance(target, WebElement):  # if given locator instead of WebElement
        target = driver.find_element(*target)  # grab element at locator
    target = visibility_of(target)(driver)
    if target and target.is_enabled():
        return target
    else:
        return False

return _predicate
"""


def muliti_click(target_element, next_element):
    def _inner(driver):
        driver.find_element(*target_element).click()
        # 第一种结果为找到，return 内容的为 webelement 对象
        # 第二种结果未找到，driver.find_element(*next_element)代码报错。会被 until 中的异常捕获异常。继续循环
        return driver.find_element(*next_element)

    return _inner


def double_click():
    driver = webdriver.Chrome()
    driver.get('https://vip.ceshiren.com/#/ui_study')
    WebDriverWait(driver, 10).until(
        muliti_click(
            (By.CSS_SELECTOR, "#primary_btn"),
            (By.XPATH, '//*[text()="该弹框点击两次后才会弹出"]')
        )
    )
    # 此方法不好使，必须自己封装 定制化显式等待
    # WebDriverWait(driver, 10).until(
    #     expected_conditions.element_to_be_clickable((By.CSS_SELECTOR, '#primary_btn'))).click()
    time.sleep(5)


if __name__ == '__main__':
    double_click()
